package com.tangguangdi.base.core.register;

import com.tangguangdi.base.common.annotation.Note;
import com.tangguangdi.base.common.basic.PermissionRule;
import com.tangguangdi.base.common.basic.PermissionTimeFilter;
import com.tangguangdi.base.common.core.*;
import com.tangguangdi.base.core.annotation.*;
import com.tangguangdi.base.core.properties.CoreProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * 单机模式服务
 */
@Slf4j
public class StandaloneModeServer implements ModeServer {

    /**
     * 核心配置
     */
    private CoreProperties coreProperties;
    /**
     * 接口文档
     */
    private AppDoc appDoc = new AppDoc();

    /**
     * 服务初始化
     * @param coreProperties 核心配置
     */
    public StandaloneModeServer(CoreProperties coreProperties) {
        this.coreProperties = coreProperties;
        appInfo();
        if (appDoc.getType() == AppType.MatrixServer) {
            List<AppController> controllers = new ArrayList<>();
            List<ApiDefinition> viewDefinitions = new ArrayList<>();
            viewDefinitions.add(new ApiDefinition().setRule(PermissionRule.None).setUrl("/v2/api-docs").setMethod("GET"));
            viewDefinitions.add(new ApiDefinition().setRule(PermissionRule.None).setUrl("/index.html").setMethod("GET"));
            viewDefinitions.add(new ApiDefinition().setRule(PermissionRule.None).setUrl("/favicon.ico").setMethod("GET"));
            viewDefinitions.add(new ApiDefinition().setRule(PermissionRule.None).setUrl("/css/*").setMethod("GET"));
            viewDefinitions.add(new ApiDefinition().setRule(PermissionRule.None).setUrl("/js/*").setMethod("GET"));
            viewDefinitions.add(new ApiDefinition().setRule(PermissionRule.None).setUrl("/img/*").setMethod("GET"));
            viewDefinitions.add(new ApiDefinition().setRule(PermissionRule.None).setUrl("/fonts/*").setMethod("GET"));
            controllers.add(new AppController().setCode("MatrixModeServer").setName("静态地址").setApiDefinitions(viewDefinitions));
            if (coreProperties.getApiDefinitions().size() > 0) {
                controllers.add(new AppController().setCode("CoreProperties").setName("手动注册").setApiDefinitions(coreProperties.getApiDefinitions()));
            }
            controllers.addAll(Scan());
            appDoc.setAppControllers(controllers);
        }
    }

    /**
     * 应用信息
     */
    private void appInfo() {
        appDoc.setCode(coreProperties.getAppCode()).setName(coreProperties.getAppName()).setType(coreProperties.getAppType()).setVersion(coreProperties.getAppVersion());
    }

    /**
     * 扫描全局
     * @return List<AppController>
     */
    private List<AppController> Scan() {
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter(new AnnotationTypeFilter(Apis.class));
        Set<BeanDefinition> beans = scanner.findCandidateComponents(coreProperties.getAppPackage());
        return controllerScanner(beans);
    }

    /**
     * 扫描控制器
     * @param beans 扫描到的Beans
     * @return List<AppController>
     */
    private List<AppController> controllerScanner(Set<BeanDefinition> beans) {
        List<AppController> controllers = new ArrayList<>();
        for (BeanDefinition bean : beans) {
            try {
                Class<?> clazz = Class.forName(bean.getBeanClassName());
                if (clazz.isAnnotationPresent(Apis.class)) {
                    Apis annotation = clazz.getAnnotation(Apis.class);
                    controllers.add(new AppController().setPrefix(annotation.path()).setCode(clazz.getSimpleName()).setName(annotation.name()).setApiDefinitions(apiScanner(annotation.path(), clazz.getDeclaredMethods())));
                }
            } catch (ClassNotFoundException e) {
                log.error("Max-Core: AppController-{} Scan Error!", bean.getBeanClassName());
            }
        }
        return controllers;
    }

    /**
     * 扫描接口
     * @param prefix 控制器前缀
     * @param methods 接口方法
     * @return List<ApiDefinition>
     */
    private List<ApiDefinition> apiScanner(String[] prefix, Method[] methods) {
        List<ApiDefinition> apiDefinitions = new ArrayList<>();
        for (Method method : methods) {
            if (method.isAnnotationPresent(GetApi.class)) apiDefinitions.addAll(getApiHandler(prefix, method));
            if (method.isAnnotationPresent(PostApi.class)) apiDefinitions.addAll(postApiHandler(prefix, method));
            if (method.isAnnotationPresent(PutApi.class)) apiDefinitions.addAll(putApiHandler(prefix, method));
            if (method.isAnnotationPresent(PatchApi.class)) apiDefinitions.addAll(patchApiHandler(prefix, method));
            if (method.isAnnotationPresent(DeleteApi.class)) apiDefinitions.addAll(deleteApiHandler(prefix, method));
        }
        return apiDefinitions;
    }

    /**
     * 接口定义处理
     * @param prefix 控制器前缀
     * @param paths 控制器路由
     * @param code 接口编码
     * @param name 接口名称
     * @param method 请求方法
     * @param rule 权限规则
     * @param timeFilter 时间规则
     * @param timeSet 时间设定
     * @return List<ApiDefinition>
     */
    private List<ApiDefinition> apiDefinitionHandler(String[] prefix, String[] paths, String code, String name, String method, PermissionRule rule, PermissionTimeFilter timeFilter, String timeSet) {
        List<ApiDefinition> apiDefinitions = new ArrayList<>();
        if (prefix.length == 0) prefix = new String[]{""};
        if (paths.length == 0) paths = new String[]{""};
        for (String base : prefix) {
            for (String path : paths) {
                ApiDefinition apiDefinition = new ApiDefinition();
                apiDefinition.setCode(code).setName(name).setPath(path).setUrl(URLConcatenation(base, path.replaceAll("\\{[^}]+\\}", "*"))).setMethod(method).setRule(rule).setTimeFilter(timeFilter).setTimeSet(timeSet);
                apiDefinitions.add(apiDefinition);
            }
        }
        return apiDefinitions;
    }

    /**
     * URL拼接
     * @param base 基础路由
     * @param path 拼接路由
     * @return url
     */
    private String URLConcatenation(String base, String path) {
        if (!base.endsWith("/")) base += "/";
        if (path.startsWith("/")) path = path.substring(1);
        String url = base + path;
        if (url.endsWith("/")) url = url.substring(0, url.length() - 1);
        return url;
    }

    /**
     * GET接口
     * @param prefix 接口前缀
     * @param method 请求方法
     * @return List<ApiDefinition>
     */
    private List<ApiDefinition> getApiHandler(String[] prefix, Method method) {
        GetApi api = method.getAnnotation(GetApi.class);
        return apiDefinitionHandler(prefix, api.path(), method.getName(), api.name(), "GET", api.rule(), api.timeFilter(), api.timeSet());
    }

    /**
     * POST接口
     * @param prefix 接口前缀
     * @param method 请求方法
     * @return List<ApiDefinition>
     */
    private List<ApiDefinition> postApiHandler(String[] prefix, Method method) {
        PostApi api = method.getAnnotation(PostApi.class);
        paramScanner(method.getParameters());
        return apiDefinitionHandler(prefix, api.path(), method.getName(), api.name(), "POST", api.rule(), api.timeFilter(), api.timeSet());
    }

    /**
     * PUT接口
     * @param prefix 接口前缀
     * @param method 请求方法
     * @return List<ApiDefinition>
     */
    private List<ApiDefinition> putApiHandler(String[] prefix, Method method) {
        PutApi api = method.getAnnotation(PutApi.class);
        return apiDefinitionHandler(prefix, api.path(), method.getName(), api.name(), "PUT", api.rule(), api.timeFilter(), api.timeSet());
    }

    /**
     * PATCH接口
     * @param prefix 接口前缀
     * @param method 请求方法
     * @return List<ApiDefinition>
     */
    private List<ApiDefinition> patchApiHandler(String[] prefix, Method method) {
        PatchApi api = method.getAnnotation(PatchApi.class);
        return apiDefinitionHandler(prefix, api.path(), method.getName(), api.name(), "PATCH", api.rule(), api.timeFilter(), api.timeSet());
    }

    /**
     * DELETE接口
     * @param prefix 接口前缀
     * @param method 请求方法
     * @return List<ApiDefinition>
     */
    private List<ApiDefinition> deleteApiHandler(String[] prefix, Method method) {
        DeleteApi api = method.getAnnotation(DeleteApi.class);
        return apiDefinitionHandler(prefix, api.path(), method.getName(), api.name(), "DELETE", api.rule(), api.timeFilter(), api.timeSet());
    }

    /**
     * 扫描入参
     * @param parameters 参数列表
     * @return List<ApiParameter>
     */
    private List<ApiParameter> paramScanner(Parameter[] parameters) {
        List<ApiParameter> apiParameters = new ArrayList<>();
        for (Parameter param : parameters) {
            apiParameters.add(paramHandler(param));
        }
        return apiParameters;
    }

    /**
     * 参数处理器
     * @param param 具体参数
     * @return ApiParameter
     */
    private ApiParameter paramHandler(Parameter param) {
        ApiParameter apiParameter = new ApiParameter();
        apiParameter.setName(param.getName());
        apiParameter.setType(paramType(param.getAnnotations()));
        return apiParameter;
    }

    /**
     * 参数类型判断
     * @param annotations 注解信息
     * @return paramType
     */
    private String paramType(Annotation[] annotations) {
        for (Annotation annotation : annotations) {
            if (annotation.annotationType().isAnnotationPresent(PathVariable.class)) return "PathVariable";
            if (annotation.annotationType().isAnnotationPresent(RequestParam.class)) return "RequestParam";
            if (annotation.annotationType().isAnnotationPresent(RequestBody.class)) return "RequestBody";
        }
        return "";
    }

    /**
     * 获取接口文档
     * @return appDoc
     */
    @Override
    public AppDoc getAppDoc() {
        return appDoc;
    }
}
